/*
    $Header: /Windows/Shell/ShellExt.root/ShellExt/src/CJunction.cpp 1     08-01-05 0:08 Ken $
    $Log: /Windows/Shell/ShellExt.root/ShellExt/src/CJunction.cpp $
 * 
 * 1     08-01-05 0:08 Ken
*/

#include "StdAfx.h"

#include <winioctl.h>

#include "reparse.h"
#include "misc.h"
#include "cjunction.h"


const TCHAR CJunction::szTypeNameS[][50]={
    TEXT("NOT_JUNCTION"),
    TEXT("SYMBOLIC_LINK"),
    TEXT("JUNCTION"),
    TEXT("MOUNT_POINT")
};

///////////static functions///////////////

//
// only NTFS can create junction
//
BOOL CJunction::CanCreateJunction(const TCHAR* pszJunctPath)
{
    DWORD    dwVlmFlg=0;
    if( (TRUE==GetVlmFlg(pszJunctPath, &dwVlmFlg))
        && (FILE_SUPPORTS_REPARSE_POINTS&dwVlmFlg) )
    {
        return TRUE;
    }

    return FALSE;
}


CJunction::JUNCTION_TYPE CJunction::JunctionType(const TCHAR* pszJunctPath, TCHAR* pSzTgtPath, DWORD dwBufLen)
{
    JUNCTION_TYPE junctionType=NOT_JUNCTION;

    DWORD dwAttr=GetFileAttributes(pszJunctPath);
    if(! (FILE_ATTRIBUTE_REPARSE_POINT & dwAttr) )
    {
        return junctionType;
    }

    //Get "file" Handle, for dir use FILE_FLAG_BACKUP_SEMANTICS flag for file operations
    DWORD dwFlagsAndAttributes=FILE_FLAG_OPEN_REPARSE_POINT;
    if( FILE_ATTRIBUTE_DIRECTORY & dwAttr)
    {
        dwFlagsAndAttributes |= FILE_FLAG_BACKUP_SEMANTICS;
    }
    HANDLE hFile=CreateFile( pszJunctPath, 0,    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
    OPEN_EXISTING, dwFlagsAndAttributes, 0 );
    if(INVALID_HANDLE_VALUE == hFile)
    {
        return junctionType;
    }

    BYTE     bReparseBuf[MAX_REPARSE_SIZE];
    PBYTE     pReparseData;
    PREPARSE_GUID_DATA_BUFFER ppReparseMPInfo = (PREPARSE_GUID_DATA_BUFFER) bReparseBuf;
    PREPARSE_DATA_BUFFER pmspReparseMPInfo = (PREPARSE_DATA_BUFFER) bReparseBuf;
    DWORD    dwRetLen;

    if( DeviceIoControl( hFile, FSCTL_GET_REPARSE_POINT, NULL, 0,
                                ppReparseMPInfo, sizeof( bReparseBuf ), &dwRetLen, NULL ))
    {

        USES_CONVERSION;     //some convert var defines

        if( IsReparseTagMicrosoft( ppReparseMPInfo->ReparseTag ))
        {
            const WCHAR cwKnlObjHd[]={L"\\??\\"};

            switch( ppReparseMPInfo->ReparseTag )
            {
            // I don't know if this is a valid reparse point
            case 0x80000000|IO_REPARSE_TAG_SYMBOLIC_LINK:
                if(NULL!=pSzTgtPath)
                {
                    pReparseData = (PBYTE) &pmspReparseMPInfo->SymbolicLinkReparseBuffer.PathBuffer;
                /*
                    _tcsncpy( name, (PWCHAR) (pReparseData + pmspReparseMPInfo->SymbolicLinkbReparseBuf.PrintNameOffset),
                    pmspReparseMPInfo->SymbolicLinkbReparseBuf.PrintNameLength );
                    name[pmspReparseMPInfo->SymbolicLinkbReparseBuf.PrintNameLength] = 0;
                */
                    if(dwBufLen > pmspReparseMPInfo->SymbolicLinkReparseBuffer.SubstituteNameLength)
                    {
                        _tcscpy( pSzTgtPath, W2T((PWCHAR) (pReparseData + pmspReparseMPInfo->SymbolicLinkReparseBuffer.SubstituteNameOffset)));
                    }
                    else
                    {
                        pSzTgtPath[0]=0;
                    }
                }
                junctionType=SYMBOLIC_LINK;
                break;

            // Mount points and junctions
            case IO_REPARSE_TAG_MOUNT_POINT:
                pReparseData = (PBYTE) &pmspReparseMPInfo->MountPointReparseBuffer.PathBuffer;
                //
                // I use a simple heuristic to differentiate mount points and junctions: a junction
                // specifies a drive-letter based target, so I look for the drive letter colon
                // in the target, which should be in the form \??\D:\target
                //
                if( L':'==(*((WCHAR*)(pReparseData + pmspReparseMPInfo->MountPointReparseBuffer.SubstituteNameOffset+sizeof(cwKnlObjHd) ))))
                {
                    junctionType=JUNCTION;
                }
                else
                {
                    junctionType=MOUNT_POINT;
                }
                if(NULL!=pSzTgtPath)
                {
                    if(dwBufLen > pmspReparseMPInfo->MountPointReparseBuffer.SubstituteNameLength-ARRAYOF(cwKnlObjHd))
                    {
                        _tcscpy( pSzTgtPath, W2T((PWCHAR) (pReparseData + pmspReparseMPInfo->MountPointReparseBuffer.SubstituteNameOffset+sizeof(cwKnlObjHd)-sizeof(WCHAR))));
                    }
                    else
                    {
                        pSzTgtPath[0]=0;
                    }
                }
                /*
                    pReparseData = (PBYTE) &pmspReparseMPInfo->MountPointbReparseBuf.PathBuffer;
                    _tcsncpy( name, (PWCHAR) (pReparseData + pmspReparseMPInfo->MountPointbReparseBuf.PrintNameOffset),
                    pmspReparseMPInfo->MountPointbReparseBuf.PrintNameLength );
                    name[pmspReparseMPInfo->MountPointbReparseBuf.PrintNameLength] = 0;
                */
                break;

            case IO_REPARSE_TAG_HSM:
                ATLTRACE(_T("%s: HIERARCHICAL STORAGE MANAGEMENT REPARSE POINT!\r\n"), pszJunctPath );
                break;

            case IO_REPARSE_TAG_SIS:
                //
                // Sinlge Instance Store installs and executes as part
                // of the Remote Installation Service (RIS)
                //
                ATLTRACE(_T("%s: SINGLE INSTANCE STORE POINT\r\n"), pszJunctPath );
                break;

#ifndef IO_REPARSE_TAG_DFS
        //defined in <winnt.h> line:5706
#define IO_REPARSE_TAG_DFS                      (0x8000000AL)
#endif
            case IO_REPARSE_TAG_DFS:
                ATLTRACE(_T("%s: DISTRIBUTED FILE SYSTEM POINT\r\n"), pszJunctPath );
                break;

            default:
                ATLTRACE(_T("%s: UNKNOWN MICROSOFT REPARSE POINT\r\n"), pszJunctPath );
                break;
            }
        }
        else
        {
            ATLTRACE(_T("%s: THIRD-PARTY REPARSE POINT\r\n"), pszJunctPath );
        }
    }


    CloseHandle(hFile);

    return junctionType;
}

BOOL CJunction::CreateJunction(const TCHAR* pszJunctPath, const TCHAR* pszTgtPath, BOOL fForce)
{
    ATLASSERT((NULL!=pszJunctPath) && (NULL!=pszTgtPath));

    BOOL fCreateDir=FALSE;

    //
    // Create the link
    if(PathFileExists(pszJunctPath) )
    {
        if(FALSE==fForce)
        {
            ATLTRACE(_T("Path %s exists!\r\n"), pszJunctPath);
            return FALSE;
        }
    }
    else
    {
        CreateDirectory( pszJunctPath, NULL );
        fCreateDir=TRUE;
    }

    HANDLE hFile = CreateFile( pszJunctPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
                                            FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL );
    if( hFile == INVALID_HANDLE_VALUE )
    {
        ATLTRACE(_T("CreateFile:%s  for reparse Faild!"), pszTgtPath);
        TRACE_SYSERR();
        return FALSE;
    }


    //
    // Make the native target name
    //
    TCHAR       szTgtNativePath[MAX_PATH];
    _stprintf_s( szTgtNativePath, _T("\\??\\%s"), pszTgtPath );
    WORD wNativePathLen=(WORD)_tcslen( szTgtNativePath );
    if ( (szTgtNativePath[wNativePathLen-1] == _T('\\'))
            && (szTgtNativePath[wNativePathLen-2] != _T(':')))
    {
        szTgtNativePath[wNativePathLen-1] = 0;
    }

    //
    // Build the reparse info
    //4 the parse info use the unicode only!!!
    //
    char    bReparseBuf[MAX_PATH*3];
    PREPARSE_MOUNTPOINT_DATA_BUFFER pReparseMPInfo = (PREPARSE_MOUNTPOINT_DATA_BUFFER) bReparseBuf;

    USES_CONVERSION;     //some convert var defines

    memset( pReparseMPInfo, 0, sizeof( *pReparseMPInfo ));
    pReparseMPInfo->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    wcscpy(pReparseMPInfo->ReparseTarget, T2CW(szTgtNativePath));
//1 !!! note wchar_len != tchar_len x 2, for MBCS!!! so "pReparseMPInfo->ReparseTargetLength = wNativePathLen * sizeof(WCHAR);" is wrong!
    pReparseMPInfo->ReparseTargetLength = wcslen(pReparseMPInfo->ReparseTarget)*sizeof(WCHAR);
    pReparseMPInfo->ReparseTargetMaximumLength = pReparseMPInfo->ReparseTargetLength + sizeof(WCHAR);
    pReparseMPInfo->ReparseDataLength = pReparseMPInfo->ReparseTargetLength + 12;

    //
    // Set the link
    //
    DWORD    dwRetLen;
    BOOL    fRet=TRUE;
    if( !DeviceIoControl( hFile, FSCTL_SET_REPARSE_POINT, pReparseMPInfo,
                                    pReparseMPInfo->ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,
                                    NULL, 0, &dwRetLen, NULL ))
    {

        ATLTRACE(_T("Error setting junction for %s:\n"), pszTgtPath );
        TRACE_SYSERR();
        fRet=FALSE;
    }

    CloseHandle( hFile );

    if( (FALSE==fRet) && (FALSE!=fCreateDir) )
    {
        RemoveDirectory( pszJunctPath );
    }

    return fRet;

}


CJunction::CJunction(const TCHAR* pszJunctPath): m_JunctionType(NOT_JUNCTION)
{
    ATLASSERT(pszJunctPath != NULL);
    m_szTgtPath[0]=0;
    _tcsncpy_s(m_szJunctPath, pszJunctPath, ARRAYOF(m_szJunctPath));
}

CJunction::CJunction(const CJunction &cjunct)
{
    _tcsncpy_s(m_szJunctPath, cjunct.m_szJunctPath, ARRAYOF(m_szJunctPath));
    _tcsncpy_s(m_szTgtPath, cjunct.m_szTgtPath, ARRAYOF(m_szTgtPath));
    m_JunctionType=cjunct.m_JunctionType;
}

CJunction& CJunction::operator =(const CJunction& cjunct)
{
    _tcsncpy_s(m_szJunctPath, cjunct.m_szJunctPath, ARRAYOF(m_szJunctPath));
    _tcsncpy_s(m_szTgtPath, cjunct.m_szTgtPath, ARRAYOF(m_szTgtPath));
    m_JunctionType=cjunct.m_JunctionType;
    return *this;
}

BOOL CJunction::Initialize(const TCHAR* pszJunctPath)
{
    if( NULL!=pszJunctPath)
    {
        _tcsncpy_s(m_szJunctPath, pszJunctPath, ARRAYOF(m_szJunctPath));
    }
    ATLASSERT(m_szJunctPath[0]!=0);
    return (m_JunctionType=JunctionType(m_szJunctPath, m_szTgtPath, ARRAYOF(m_szTgtPath)))!=NOT_JUNCTION;
}


BOOL CJunction::CreateJunction(const TCHAR* pszTgtPath)
{
    ATLASSERT((pszTgtPath[0]!=0) && (m_szJunctPath[0]!=0));

    if(TRUE==CreateJunction(m_szJunctPath, pszTgtPath))
    {
        m_JunctionType=JUNCTION;
        _tcsncpy_s(m_szTgtPath, pszTgtPath, ARRAYOF(m_szTgtPath));
        return TRUE;
    }

    return FALSE;
}

const TCHAR* CJunction::GetJunctionTypeName()
{
    if(m_JunctionType>CJunction::MOUNT_POINT)
    {
        return NULL;
    }
    return szTypeNameS[m_JunctionType];
}


